#include "Col.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "ObjList.h"
#include "Class.h"
		
//---- private imports ----

#include "IterIter.h"

#include "Error.h"
#include "Storage.h"

//---- ObjLink -----------------------------------------------------------------

NewMetaImpl(ObjLink,Object, (TP(next), TP(prev), TP(obj)));

ObjLink::ObjLink(Object *op, ObjLink *lnk)
{
	obj= op;
	next= lnk;
	prev= lnk->prev;
	prev->next= this;
	next->prev= this;
}

void ObjLink::FreeAll()
{
	if (obj) {
		obj->FreeAll();
		SafeDelete(obj);
	}
}

//---- ObjList ----------------------------------------------------------------

NewMetaImpl(ObjList,SeqCollection, (TP(head)));

ObjList::ObjList()
{
	head= new ObjLink;
}

ObjList::~ObjList()
{
	register ObjLink *lnk, *tlk;

	lnk= head->Next();
	while (lnk != head) {
		tlk= lnk;
		lnk= lnk->Next();
		delete tlk;
	}
	delete head;
}

void ObjList::InitNew()
{
	SeqCollection::InitNew();
	head= new ObjLink;
}

void ObjList::Empty(int)
{
	register ObjLink *ll, *l;

	l= head->next;
	while (l != head) {
		ll= l;
		l= l->next;
		delete ll;
	}
	head->next= head;
	head->prev= head;
	size= 0;
	if (HasIterators())
		ForEachIterDo(ObjListIter, OnEmpty());
}

void ObjList::FreeAll()
{
	register ObjLink *l, *ll;

	l= head->next;
	while (l != head) {
		ll= l;
		l= l->next;
		ll->FreeAll();
		delete ll;
	}
	head->next= head;
	head->prev= head;
	size= 0;
	if (HasIterators())
		ForEachIterDo(ObjListIter, OnEmpty());
}

//---- accessing ----

Iterator *ObjList::MakeIterator(bool dir, void *placement)
{
	if (dir == cIterForward)
		return new(placement) ObjListIter(this);
	return new(placement) RevObjListIter(this);
}

//---- primitives ----

Object *ObjList::primAt(int idx)
{
	return LinkAt(idx)->GetObject();
}

Object *ObjList::primRemoveAt(int idx)
{
	ObjLink *lnk= LinkAt(idx);
	Object *removed= lnk->GetObject();
	RemoveLink(lnk);

	size--;
	Changed();
	return removed;
}

Object *ObjList::primAddAt(Object *op, int idx)
{
	InsertLink(op, LinkAt(idx));
	return 0;
}

Object *ObjList::primPutAt(Object *op, int idx)
{
	ObjLink *lnk= LinkAt(idx);
	Object *replaced= lnk->GetObject();
	lnk->SetObject(op);
	if (op == replaced)
		Changed();
	return replaced;
}

Object *ObjList::primIndexOf(Object *op, int offset, int *idx)
{
	int i;

	ObjLink *lnk= FindLink(op, i);
	if (lnk) {
		int edx= i + offset;
		if (edx >= 0 && edx < Size()) {
			lnk= LinkByOffset(lnk, offset);
			if (idx)
				*idx= edx;
			return lnk->GetObject();
		}
	}
	if (idx)
		*idx= -1;
	return 0;
}

Object *ObjList::primIndexOfPtr(Object *op, int offset, int *idx)
{
	int i;

	ObjLink *lnk= FindLinkPtr(op, i);
	if (lnk) {
		int edx= i + offset;
		if (edx >= 0 && edx < Size()) {
			lnk= LinkByOffset(lnk, offset);
			if (idx)
				*idx= edx;
			return lnk->GetObject();
		}
	}
	if (idx)
		*idx= -1;
	return 0;
}

Object *ObjList::AddRelative(Object *op, EqualType eq, int offset, Object *to)
{
	ObjLink *lnk;
	int idx;

	if (eq)
		lnk= FindLink(to, idx);
	else
		lnk= FindLinkPtr(to, idx);
	if (lnk) {
		int edx= idx + offset;
		if (edx >= 0 && edx <= Size()) {
			lnk= LinkByOffset(lnk, offset);
			InsertLink(op, lnk);
			return 0;
		}
		return op;
	}
	return op;
}

Object *ObjList::RemoveRelative(Object *op, EqualType eq, int offset)
{
	ObjLink *lnk;
	int idx;

	if (eq)
		lnk= FindLink(op, idx);
	else
		lnk= FindLinkPtr(op, idx);
	if (lnk) {
		int edx= idx + offset;
		if ( edx >= 0 && edx < Size() ) {
			lnk= LinkByOffset(lnk, offset);
			Object *removed= lnk->GetObject();
			RemoveLink(lnk);
			size--;
			Changed();
			return removed;
		}
		return 0;
	}
	return 0;
}

ObjLink *ObjList::NewLink(Object *op, ObjLink *prev)
{
	return new ObjLink(op, prev);
}

void ObjList::DeleteLink(ObjLink *lnk)
{
	delete lnk;
}

//---- implementation ----

ObjLink *ObjList::FindLink(Object *op, int &idx)
{
	register ObjLink *lnk;

	idx= 0;
	lnk= head->next;
	while (lnk != head) {
		if (lnk->GetObject()->IsEqual(op))
			return lnk;
		lnk= lnk->next;
		idx++;
	}
	return 0;
}

ObjLink *ObjList::FindLinkPtr(Object *op, int &idx)
{
	idx= 0;
	register ObjLink *lnk= head->next;
	while (lnk != head) {
		if (lnk->GetObject() == op)
			return lnk;
		lnk= lnk->next;
		idx++;
	}
	return 0;
}

ObjLink *ObjList::LinkAt(int idx)
{
	register int i= 0;
	register ObjLink *lnk= head->Next();
	while (i < idx && lnk != head) {
		lnk= lnk->Next();
		i++;
	}
	return lnk;
}

ObjLink *ObjList::LinkByOffset(ObjLink *lnk, int offset)
{
	if (offset >= 0) {
		while (offset > 0) {
			offset--;
			lnk= lnk->Next();
		}
	} else {
		while (offset < 0) {
			offset++;
			lnk= lnk->Prev();
		}
	}
	return lnk;
}

void ObjList::RemoveLink(ObjLink *lnk)
{
	if (HasIterators())
		ForEachIterDo(ObjListIter, OnRemove(lnk));
	lnk->prev->next= lnk->next;
	lnk->next->prev= lnk->prev;
	DeleteLink(lnk);
}

void ObjList::InsertLink(Object *op, ObjLink *prev)
{
	NewLink(op, prev);
	size++;
	Changed();
}

//---- ObjListIter ------------------------------------------------------------

ObjListIter::ObjListIter(ObjList *s)
{
	list= s;
	ce= 0;
}

ObjListIter::~ObjListIter()
{
	Terminate();
}

void ObjListIter::Reset()
{
	Iterator::Reset();
	ce= 0;
}

Object* ObjListIter::operator()()
{
	if (! Activated())
		if (Activate())
			ce= list->head;
	if (! Terminated()) {
		ce= ce->Next();
		Object *op= ce->GetObject();
		if (op)
			return op;
		Terminate();
		return 0;
	}
	return 0;
}

Container *ObjListIter::GetContainer()
{
	return list;
}

void ObjListIter::Detach()
{
	list= 0;
	Iterator::Detach();
}

void ObjListIter::OnEmpty()
{
	if (IsActive())
		ce= list->head;
}

void ObjListIter::OnRemove(ObjLink *lnk)
{
	if (IsActive() && lnk == ce)
		ce= ce->prev;
}

//---- RevObjListIter ---------------------------------------------------------

RevObjListIter::RevObjListIter(ObjList *s) : ObjListIter(s)
{
}

Object* RevObjListIter::operator()()
{
	if (! Activated())
		if (Activate())
			ce= list->head;
	if (! Terminated()) {
		ce= ce->Prev();
		Object *op= ce->GetObject();
		if (op)
			return op;
		Terminate();
		return 0;
	}
	return 0;
}

void RevObjListIter::OnRemove(ObjLink *lnk)
{
	if (IsActive() && lnk == ce)
		ce= ce->Next();
}

